package net.jwtools.tmt.ui;

import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JPanel;

import net.jwtools.tmt.domain.IVisit;
import net.jwtools.tmt.domain.TerritoryType;
import net.jwtools.tmt.util.FilterComponent;
import net.jwtools.tmt.util.TerritoryTypeComparator;

import org.jdesktop.swingx.JXLabel;

import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.GroupingList;
import ca.odell.glazedlists.matchers.AbstractMatcherEditor;
import ca.odell.glazedlists.matchers.Matcher;
import ca.odell.glazedlists.matchers.MatcherEditor;
import ca.odell.glazedlists.swing.GlazedListsSwing;

/**
 * A MatcherEditor that produces Matchers that filters the visits based on
 * the selected territory types.
 */
class TerritoryTypeMatcherEditor extends AbstractMatcherEditor<IVisit> implements
		ActionListener, FilterComponent<IVisit> {

	/** A panel housing a checkbox for each type. */
	private JPanel checkBoxPanel = new JPanel();
	
	/** A checkbox for each displayed type. */
	private final Map<TerritoryType, JCheckBox> typeCheckBoxes = new LinkedHashMap<TerritoryType, JCheckBox>();

	/** Territorys grouped together by type. */
	private final GroupingList<IVisit> visitsByTerritoryType;
	private final EventList<List<IVisit>> visitsByTerritoryTypeSwingThread;

	public TerritoryTypeMatcherEditor(EventList<IVisit> visits) {
		this.checkBoxPanel.setLayout(new BoxLayout(this.checkBoxPanel,BoxLayout.Y_AXIS));
		// group the Territories according to their type
		visitsByTerritoryType = new GroupingList<IVisit>(visits,
				new TerritoryTypeComparator());
		this.visitsByTerritoryTypeSwingThread = GlazedListsSwing
				.swingThreadProxyList(visitsByTerritoryType);

		this.typeCheckBoxes.put(TerritoryType.URBAN, buildCheckBox("Urban"));
		this.typeCheckBoxes.put(TerritoryType.RURAL, buildCheckBox("Rural"));
		this.typeCheckBoxes.put(TerritoryType.FLAT, buildCheckBox("Flat"));
		this.typeCheckBoxes.put(TerritoryType.BUSINESS,	buildCheckBox("Business"));
		
		this.checkBoxPanel.setOpaque(false);
		
		this.checkBoxPanel.add(new JXLabel("Filter"));
		// add each checkbox to the panel and start listening to selections
		for (Iterator<JCheckBox> iter = typeCheckBoxes.values().iterator(); iter
				.hasNext();) {
			JCheckBox checkBox = iter.next();
			checkBox.addActionListener(this);
			this.checkBoxPanel.add(checkBox);
		}
	}

	/**
	 * Returns the component responsible for editing the type filter.
	 */
	public JComponent getComponent() {
		return this.checkBoxPanel;
	}

	public MatcherEditor<IVisit> getMatcherEditor() {
		return this;
	}

	/**
	 * A convenience method to build a type checkbox with the given name.
	 */
	private static JCheckBox buildCheckBox(String name) {
		final JCheckBox checkBox = new JCheckBox(name, true);
		checkBox.setName(name);
		checkBox.setOpaque(false);
		checkBox.setFocusable(false);
		checkBox.setMargin(new Insets(10, 10, 0, 20));
		return checkBox;
	}

	/**
	 * Returns a TypeMatcher which matches Territories if their type is one of
	 * the selected types.
	 */
	private TypeMatcher buildMatcher() {
		final Set<TerritoryType> allowedTypes = new HashSet<TerritoryType>();

		for (Iterator<Map.Entry<TerritoryType, JCheckBox>> iter = typeCheckBoxes
				.entrySet().iterator(); iter.hasNext();) {
			Map.Entry<TerritoryType, JCheckBox> entry = iter.next();
			if (entry.getValue().isSelected())
				allowedTypes.add(entry.getKey());
		}

		return new TypeMatcher(allowedTypes);
	}

	public void actionPerformed(ActionEvent e) {
		// determine if the checkbox that generated this ActionEvent is freshly
		// checked or freshly unchecked
		// - we'll use that information to determine whether this is a
		// constrainment or relaxation of the matcher
		final boolean isCheckBoxSelected = ((JCheckBox) e.getSource())
				.isSelected();

		// build a TypeMatcher
		final TypeMatcher typeMatcher = this.buildMatcher();

		// fire a MatcherEvent of the appropriate type
//		if (typeMatcher.getStateCount() == 0)
//			this.fireMatchNone();
//		else if (typeMatcher.getStateCount() == this.typeCheckBoxes.size())
//			this.fireMatchAll();
		if (isCheckBoxSelected)
			this.fireRelaxed(typeMatcher);
		else
			this.fireConstrained(typeMatcher);
	}

	/**
	 * A TypeMatcher returns <tt>true</tt> if the type of the Territory is one
	 * of the viewable type selected by the user.
	 */
	private static class TypeMatcher implements Matcher<IVisit> {
		private final Set<TerritoryType> allowedTypes;

		public TypeMatcher(Set<TerritoryType> allowedTypes) {
			this.allowedTypes = allowedTypes;
		}


		public boolean matches(IVisit visit) {
			return this.allowedTypes.contains(visit.getTerritory().getType());
		}
	}
}